/**
 * 
 */
package mdp.solve;

import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;

import dd.discrete.DD;

import util.Pair;
import util.Timer;

import mdp.id.AADDHandler;
import mdp.id.ADDHandler;
import mdp.id.LogicRDDL2DD;
import mdp.id.LogicRDDL2DD.DEBUG;
import mdp.id.LogicRDDL2DD.ORDER;

/**
 * @author NR
 *
 * TODO
 */
public class ValueIterationAADD<T extends LogicRDDL2DD> implements Runnable{

	private static double	EPSILON	= 0;
	private T _handler;
	private ArrayBlockingQueue<Pair<T, Integer>> _bq = null;
	/**
	 * 
	 */
	
	public ValueIterationAADD(T handler, String domain, String instance, double epsilon,
			ArrayBlockingQueue<Pair<T, Integer>> bq) {
		_bq = bq;
		EPSILON = epsilon;
		_handler = handler;
		_handler.buildFromFile(domain, instance);
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
//	@Override
	public void run() {
		
		int _valueDD = _handler.DD_ZERO;
//		int _policy = _handler.DD_ZERO;
		
		int iter = 0;
		boolean done = false;
		boolean lastiter = false;
		
		Timer cptTimer = new Timer();
		ArrayList<Map<Integer, Boolean>> regOrder = _handler.convertToBN(null,null,true);
		cptTimer.StopTimer();
		
		Timer solutionTimer = new Timer();
		
		while( !done ) {
			solutionTimer.ResumeTimer();
			
			int Vprev = _valueDD;
			_handler.saveThis(Vprev);
			int Vprimed = _handler.swapForPrimes(_valueDD);
			_handler.saveThis(Vprimed);

			for( Map<Integer, Boolean> jointAct : regOrder  ) {
				Pair<Integer, Integer> vq = _handler.regress(Vprimed, jointAct, true, _valueDD);//, _policy);
				
				int Vnext = _handler._context.applyInt(_valueDD, vq._o1, DD.ARITH_MAX);
				_valueDD = Vnext;
				
//				if( lastiter ) {
//					int diff = _handler._context.applyInt(Vnext, vq._o1, DD.ARITH_MINUS);
//					
//					_handler.saveThis(_policy);
//					_handler.saveThis(_valueDD);
//					
//					int actzero = _handler.thresholdAt(diff, 0.0d);
//					
//					_handler.forgetThis(_policy);
//					_handler.forgetThis(_valueDD);
//					
//					int actone = _handler._context.applyInt(_handler.DD_ONE, actzero
//							, DD.ARITH_MINUS); 
//					
//					long actid = _handler.getJointActionID(jointAct);
//					
//					_policy = _handler._context.applyInt(
//							_handler._context.applyInt(_policy, actzero, DD.ARITH_PROD)
//							,_handler._context.scalarMultiply(actone, actid), 
//							DD.ARITH_SUM);	
//				}
				
				_handler.flushCaches(false, _valueDD);//, _policy);
			}
			 
			
			int diff = _handler._context.applyInt(_valueDD, Vprev, DD.ARITH_MINUS);
			
			double error = Math.max(
					Math.abs(_handler.getMax(diff)), 
					Math.abs(_handler.getMin(diff) == _handler.getNegInf() ? 0 : _handler.getMin(diff)));
			++iter;
			
			if( lastiter ) {
				done = true;
			}
			
			if( error < EPSILON || _handler.isHorizon(iter+1) ) {
				lastiter = true;
			}
			
			_handler.forgetThis(Vprev);
			_handler.forgetThis(Vprimed);
			_handler.flushCaches(false, _valueDD);//, _policy);
			
			solutionTimer.PauseTimer();
			
			System.out.println("Iteration " + iter + " " + error + " " + _handler.getSize(_valueDD) + " " + solutionTimer.GetElapsedTimeInMinutes());

		}
//		System.out.println(timetosolve + " "+ _handler.getSize(_valueDD));
		
		_handler.setValueDD(_valueDD);
//		_handler.setPolicyDD(_policy);
		
		ArrayList stats = new ArrayList();
//		_handler.playDD(_policy);
		
		stats.add(0);
		stats.add(0);
		stats.add(0);
		
		stats.add(cptTimer.GetElapsedTimeInMinutes());
		stats.add(solutionTimer.GetElapsedTimeInMinutes());
		stats.add(_handler.getSize(_valueDD));
		stats.add(0);
		
		
		System.out.println(stats);
//		_handler.showGraph(_policy);
		
//		if( _bq != null ) {
//			try
//			{
////				_bq.put(_handler.getADD(_valueDD));
////				_bq.put(new Pair<T, Integer>(_handler, _policy));
//			} catch (InterruptedException e)
//			{
//				e.printStackTrace();
//			}
//		
//		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		AADDHandler handle = new AADDHandler(DEBUG.PROBLEM_INFO, ORDER.GUESS);
		Runnable vi = 
			new ValueIterationAADD<AADDHandler>(handle, args[0], args[1], 
					Double.parseDouble(args[2]), null);
		Thread viWorker = new Thread(vi, "VI");
		viWorker.start();
		viWorker.join();
	}
	

}
